﻿using BDRUtils.Cached;
using BDRUtils.Extensions;
using System;

namespace BDRUtils.VerifyCode
{
    /// <summary>
    /// 验证码服务
    /// </summary>
    public class VetifyCodeSevice : IVerifyCode
    {
        private readonly ICached cached;

        public VetifyCodeSevice(ICached cached)
        {
            this.cached = cached;
        }

        /// <summary>
        /// 校验验证码
        /// </summary>
        /// <param name="key"></param>
        /// <param name="code"></param>
        /// <remarks>
        /// 返回值 = -1，表示已过期
        /// 返回值 = 0，表示验证码错误
        /// 返回值 = 1，表示验证码正确
        /// 返回值 = 2，表示请求过于频繁
        /// </remarks>
        /// <returns></returns>
        public int CheckCode(string key, string code)
        {
            if (string.IsNullOrWhiteSpace(code))
                return 0;
            var value = cached.Get<string>(key);
            if (value == null)
                return -1;

            if (code.Equals(value))
            {
                cached.Remove(key);//验证正确后移除
            }
            return code.Equals(value) ? 1 : 0;
        }

        /// <summary>
        /// 24小时限制的缓存key
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        private string GetMaxLimitKey(string key) => $"VerifyCode_Get-{key}";

        /// <summary>
        /// 获取验证码
        /// </summary>
        /// <param name="key"></param>
        /// <param name="minutes"></param>
        /// <param name="maxNumPerAccount"></param>
        /// <returns></returns>
        public string GetCode(string key, int minutes, int maxNumPerAccount)
        {
            var maxLimitKey = GetMaxLimitKey(key);
            int keyNum = cached.Get<int>(maxLimitKey);
            if (keyNum >= maxNumPerAccount)
                throw new Exception($"每个账号24小时内最多获取{maxNumPerAccount}个验证码，您已经获取了{keyNum}个");

            cached.Set(maxLimitKey, ++keyNum, TimeSpan.FromDays(1), false);

            var limitNum = 5;
            var numKey = $"num_{key}";
            var firstTimeKey = $"ftime_{key}";
            var sendStatuKey = $"statu_{key}";

            #region 第一次获取验证码时间

            var ftime = cached.Get<string>(firstTimeKey);
            if (string.IsNullOrWhiteSpace(ftime))
            {

                ftime = cached.Get<string>(firstTimeKey);
                if (string.IsNullOrWhiteSpace(ftime))
                {
                    ftime = DateTime.Now.ToString();
                    cached.Set(firstTimeKey, ftime, TimeSpan.FromMinutes(minutes));
                }
            }
            else
            {
                cached.Set(firstTimeKey, ftime, TimeSpan.FromMinutes(minutes));
            }

            #endregion

            #region 一分钟尝试次数超过限定次数

            var numObj = cached.Get<int>(numKey);
            int num = 1;
            if (numObj > 0)
            {
                num = numObj;
                if (!ftime.IsNullOrWhiteSpace())
                {
                    num++;
                    if (num > limitNum)
                    {
                        cached.Set(firstTimeKey, ftime, TimeSpan.FromMinutes(1));
                        cached.Set(sendStatuKey, DateTime.Now.ToString(), TimeSpan.FromMinutes(5));
                    }
                }
                else
                    num = 1;
            }
            cached.Set(numKey, num, TimeSpan.FromMinutes(1));

            #endregion

            #region 

            var statuObj = cached.Get<string>(sendStatuKey);
            if (!statuObj.IsNullOrWhiteSpace() && statuObj.ToDateTime().HasValue && statuObj.ToDateTime().Value.AddMinutes(5) < DateTime.Now)
            {
                return "busy";
            }

            #endregion

            var verifyCode = cached.Get<string>(key);
            if (string.IsNullOrWhiteSpace(verifyCode))
            {
                Random r = new Random();
                int ranNum = r.Next(100000, 999999);
                cached.Set(key, ranNum.ToString(), TimeSpan.FromMinutes(minutes));
                return ranNum.ToString();
            }

            return verifyCode;
        }
    }
}
